Appearance
[English version](/posts/c教程/en-US/05-Input Output and Files/045-Buffered IO with fgets and fputs)
当您的程序读取和写入数据时,它并不总是直接进入磁盘或终端,而是使用缓冲区。缓冲区是临时保存数据的小内存块,通过减少系统执行缓慢 I/O 操作的频率来提高性能。
C 的标准 I/O 库(<stdio.h>)会自动为您处理此问题。在本节中,您将了解缓冲的工作原理以及如何使用fgets,fputs,以及有效管理它的相关功能。
什么是缓冲 I/O?
C 库不是一次读取或写入一个字符,而是:
- 填充缓冲区(用于输入)或
- 收集一批字符(用于输出)
当缓冲区已满或刷新时,数据在程序和文件或终端之间移动。
这就是为什么有时printf()输出不会立即出现,它会在缓冲区中等待,直到发生换行或刷新。
输入:fgets()
fgets()从流中读取整行(包括空格)并将其存储在字符串中。
char *fgets(char *str, int size, FILE *stream);-str:存储线的位置 -size:读取的最大字符数(包括\0) -stream:从哪里读取(例如stdin或文件指针)
例子:
#include <stdio.h>
int main(void) {
char line[100];
printf("Enter a sentence: ");
if (fgets(line, sizeof(line), stdin))
printf("You said: %s", line);
return 0;
}输入:
C is beautiful.输出:
You said: C is beautiful.如果输入超出缓冲区,fgets之后停止阅读size - 1字符以防止溢出,并自动以空值终止字符串。
输出:fputs()
fputs()将字符串写入流。
int fputs(const char *str, FILE *stream);例子:
#include <stdio.h>
int main(void) {
FILE *fp = fopen("output.txt", "w");
if (!fp) {
perror("Open failed");
return 1;
}
fputs("Buffered I/O makes C fast.\n", fp);
fputs("fgets and fputs are line-based tools.\n", fp);
fclose(fp);
printf("Wrote to output.txt\n");
return 0;
}输出文件:
Buffered I/O makes C fast.
fgets and fputs are line-based tools.为什么 fgets 比 scanf("%s", …) 更安全
-fgets()尊重缓冲区边界
- 它可以正确读取空格和制表符
- 它可以防止溢出的未定义行为
避免这种情况:
scanf("%s", buffer); // stops at first space and may overflow更喜欢这个:
fgets(buffer, sizeof(buffer), stdin);使用 fputs 写入 stdout 或 stderr
fputs()适用于任何输出流,而不仅仅是文件。
fputs("Message to screen.\n", stdout);
fputs("Error to stderr!\n", stderr);您甚至可以在 shell 中重定向它们:
./program > result.txt 2> error.log逐行读取文件
以下是如何使用以下方法安全地读取文件fgets():
#include <stdio.h>
int main(void) {
FILE *fp = fopen("poem.txt", "r");
if (!fp) {
perror("Failed to open file");
return 1;
}
char line[200];
while (fgets(line, sizeof(line), fp))
printf("%s", line);
fclose(fp);
return 0;
}小代码:复制文件
一个最小的文件复制器使用fgets和fputs:
#include <stdio.h>
int main(void) {
FILE *in = fopen("input.txt", "r");
FILE *out = fopen("copy.txt", "w");
if (!in || !out) {
perror("File error");
return 1;
}
char buf[256];
while (fgets(buf, sizeof(buf), in))
fputs(buf, out);
fclose(in);
fclose(out);
printf("Copied successfully.\n");
return 0;
}缓冲液冲洗
有时您需要手动刷新输出:
fflush(stdout);这对于必须立即显示消息的交互式程序非常有用。
要完全禁用缓冲(例如,用于日志记录):
setbuf(stdout, NULL);为什么它很重要
缓冲 I/O 平衡速度和安全性:
-fgets防止溢出 -fputs确保高效输出
- 缓冲最大限度地减少缓慢的磁盘和控制台调用
这使得 C 既低级又高性能,而无需强迫您自己管理每个字节。
自己尝试一下
- 编写一个程序,从 stdin 读取行并将其写入新文件。
- 计算在 EOF 之前读了多少行。
- 使用以下命令打印每一行的行号
fgets和printf. - 试验缓冲区大小,尝试 16 与 256 字节并注意性能差异。
- 在交互式日志记录程序的每一行之后刷新输出。
fgets和fputs为您的文件和控制台 I/O 提供安全、基于行的基础。接下来,您将学习如何使用正确处理错误errno,perror, 和strerror,用 C 语言编写可靠系统程序的基本工具。
